import java.io.BufferedReader;
import java.io.File;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.json.simple.JSONObject;

import com.lightstreamer.interfaces.data.DataProviderException;
import com.lightstreamer.interfaces.data.FailureException;
import com.lightstreamer.interfaces.data.ItemEventListener;
import com.lightstreamer.interfaces.data.SmartDataProvider;
import com.lightstreamer.interfaces.data.SubscriptionException;


public class ChatDataAdapter implements SmartDataProvider {

    /**
     * A static map, to be used by the Metadata Adapter to find the data
     * adapter instance; this allows the Metadata Adapter to forward client
     * messages to the adapter.
     * The map allows multiple instances of this Data Adapter to be included
     * in different Adapter Sets. Each instance is identified with the name
     * of the related Adapter Set; defining multiple instances in the same
     * Adapter Set is not allowed.
     */
    public static final ConcurrentHashMap<String, ChatDataAdapter> feedMap =
        new ConcurrentHashMap<String, ChatDataAdapter>();

    /**
     * Private logger; a specific "LS_demos_Logger.Chat" category
     * should be supplied by log4j configuration.
     */
    private Logger logger;

    /**
     * The listener of updates set by Lightstreamer Kernel.
     */
    private ItemEventListener listener;

    /**
     * Used to enqueue the calls to the listener.
     */
    private final ExecutorService executor;

    public ChatDataAdapter() {
        executor = Executors.newSingleThreadExecutor();
    }

    public void init(Map params, File configDir) throws DataProviderException {
        logger = Logger.getLogger("LS_demos_Logger.Chat");

        // Read the Adapter Set name, which is supplied by the Server as a parameter
        String adapterSetId = (String) params.get("adapters_conf.id");

        // Put a reference to this instance on a static map
        // to be read by the Metadata Adapter
        feedMap.put(adapterSetId, this);

        // Adapter ready
        logger.info("ChatDataAdapter ready");

    }

    public void subscribe(String item, Object handle, boolean arg2)
            throws SubscriptionException, FailureException {

        if (item.startsWith("game_player_connect_events_")) {
        	sendConnectionStatusChangeNotification(item, "connected");
        }

    }


    public void unsubscribe(String item) throws SubscriptionException,
        FailureException {
    	if (item.startsWith("game_player_connect_events_")) {
        	sendConnectionStatusChangeNotification(item, "disconnected");
        }
    }

    public boolean isSnapshotAvailable(String arg0)
            throws SubscriptionException {
        //This adapter does not handle the snapshot.
        //If there is someone subscribed the snapshot is kept by the server
        return false;
    }


    public void setListener(ItemEventListener listener) {
        this.listener = listener;

    }


    /**
     * Accepts message submission for the unique chat room.
     * The sender is identified by an IP address and a nickname.
     * @param playerId
     */
    public void sendMessage(String player, final String gameId, String message, String timestamp, String playerId) {
        final HashMap<String, String> update = new HashMap<String, String>();
        update.put("player", player);
        update.put("message", message);
        update.put("time", timestamp);
        update.put("player_id", playerId);

        Runnable updateTask = new Runnable() {
            public void run() {
            	logger.log(Level.SEVERE, "sending the following chat to my peeeps out there" + update);
            	listener.update("game_chats_" + gameId, update, false);
            }
        };
        executor.execute(updateTask);
    }

    public void sendPlay(final String gameId, final String playerId, final String playerName,
    		String playType, Map<String, String> metadata) {
        final HashMap<String, String> update = new HashMap<String, String>();
        update.put("player_name", playerName);
        update.put("player_id", playerId);
        update.put("play_type", playType);

        JSONObject metadataJson = new JSONObject();
        metadataJson.putAll(metadata);
        update.put("metadata", metadataJson.toJSONString());

        Runnable updateTask = new Runnable() {
            public void run() {
            	logger.log(Level.SEVERE, "sending the following play to my peeeps out there" + update);
            	listener.update("game_plays_" + gameId, update, false);
            }
        };
        executor.execute(updateTask);
    }

    public void sendConnectionStatusChangeNotification(final String itemName,
			final String status) {
		final HashMap<String, String> data = new HashMap<String, String>();
		String[] split = itemName.split("_");
		final String gameId = split[4];
		final String playerId = split[5];

		data.put("player_id", playerId);
		data.put("game_id", gameId);
		data.put("status", status);
		logger.log(Level.SEVERE, "sending the following to django" + data);
		postStatusUpdateToDjango(playerId, gameId, status);

		Runnable updateTask = new Runnable() {
			public void run() {
				logger.log(Level.SEVERE, "sending the following to connect eventes" + data);
				listener.update("game_connect_events_" + gameId, data, false);
				if (gameId.equals("0")) {
					listener.update("player_presence_events_" + playerId, data, false);
				}
			}
		};
		executor.execute(updateTask);
	}

	public void sendPlayerProfileUpdateMessage(String playerId, String field, String oldValue,
			String newValue, final String gameIds) {
		final HashMap<String, String> data = new HashMap<String, String>();
		data.put("player_id", playerId);
		data.put("field", field);
		data.put("old_value", oldValue);
		data.put("new_value", newValue);

		Runnable updateTask = new Runnable() {
			public void run() {
				for (String gameId: gameIds.split(",")){
					logger.log(Level.SEVERE, "sending the following  to game_player_profile_updates_" + gameId + data);
					listener.update("game_player_profile_updates_" + gameId, data, false);
				}
			}
		};
		executor.execute(updateTask);
	}

	public void sendAddFriendMessage(String playerName, final String friendId, String playerId, String playId) {
		final HashMap<String, String> data = new HashMap<String, String>();
		data.put("player_name", playerName);
		data.put("player_id", playerId);
		data.put("play_id", playId);

		Runnable updateTask = new Runnable() {
			public void run() {
				logger.log(Level.SEVERE, "sending the following to " + "player_" + friendId + "_friend_requests" + "friend requests" + data);
				listener.update("player_" + friendId + "_friend_requests", data, false);
			}
		};
		executor.execute(updateTask);

	}

	public void sendAcceptFriendMessage(String playerName, final String friendId, String playerId) {
		final HashMap<String, String> data = new HashMap<String, String>();
		data.put("player_name", playerName);
		data.put("player_id", playerId);

		Runnable updateTask = new Runnable() {
			public void run() {
				logger.log(Level.SEVERE, "sending the following to " + "player_" + friendId + "_friend_requests" + "friend requests" + data);
				listener.update("player_" + friendId + "_friend_accepts", data, false);
			}
		};
		executor.execute(updateTask);

	}

    private void postStatusUpdateToDjango(String playerId, String gameId, String status) {
		try {
		    // Construct data
		    String data = URLEncoder.encode("player_id", "UTF-8") + "=" + URLEncoder.encode(playerId, "UTF-8");
		    data += "&" + URLEncoder.encode("game_id", "UTF-8") + "=" + URLEncoder.encode(gameId, "UTF-8");
		    data += "&" + URLEncoder.encode("status", "UTF-8") + "=" + URLEncoder.encode(status, "UTF-8");

		    logger.log(Level.SEVERE, "sending the following to django for reals" + data);
		    // Send data
		    URL url = new URL("http://www.spargame.com/updateConnectedStatus/");
		    URLConnection conn = url.openConnection();
		    conn.setDoOutput(true);
		    OutputStreamWriter wr = new OutputStreamWriter(conn.getOutputStream());
		    wr.write(data);
		    wr.flush();

		    BufferedReader rd = new BufferedReader(new InputStreamReader(conn.getInputStream()));
		    String line;
		    while ((line = rd.readLine()) != null) {
		       logger.log(Level.SEVERE, line);
		    }
		    wr.close();
		    rd.close();
		} catch (Exception e) {
			e.printStackTrace();
			logger.log(Level.SEVERE, "Ooppsies on sending to django");
		}
	}

    public void subscribe(String arg0, boolean arg1)
        throws SubscriptionException, FailureException {
    }

}